type {{ name }} struct {
    variant string
    {% for v in variants %}
    variant_{{ v.name }} *{{ v.type_.serialize_type(pkg) }}
    {% endfor %}
}

func (u *{{ name }}) Decode(holder *cffi.CFFIValueUnionVariant, typeMap baml.TypeMap) {
    valueHolder := holder.Value
    variantName := holder.VariantName
	switch variantName {
    {% for v in variants -%}
	    case "{{ v.cffi_name }}":
            u.variant = "{{ v.name }}"
            value := {{ v.type_.decode_from_any("valueHolder", pkg) }}
            u.variant_{{ v.name }} = &value
    {% endfor %}
    default:
        panic(fmt.Sprintf("invalid union variant: %s", variantName))
	}
}


func (u {{ name }}) Encode() (*cffi.CFFIValueHolder, error) {
    switch u.variant {
    {% for v in variants %}
    case "{{ v.name }}":
        return baml.EncodeUnion(u.BamlEncodeName, "{{ v.cffi_name }}", *u.variant_{{ v.name }})
    {% endfor %}
    case "":
        return nil, fmt.Errorf("invalid union variant: [unset]")
    }

    return nil, fmt.Errorf("invalid union variant: %s", u.variant)
}

func (u {{ name }}) BamlTypeName() string {
    return "{{ name }}"
}

func (u {{ name }}) BamlEncodeName() *cffi.CFFITypeName {
    return &cffi.CFFITypeName{
        Name:      "{{ cffi_name }}",
        Namespace: {{ pkg.namespace() }},
    }
}

func (u {{ name }}) MarshalJSON() ([]byte, error) {
    switch u.variant {
    {% for v in variants %}
    case "{{ v.name }}":
        return json.Marshal(u.variant_{{ v.name }})
    {% endfor %}
    }

    return nil, fmt.Errorf("invalid union variant: %s", u.variant)
}

func (u *{{ name }}) UnmarshalJSON(data []byte) error {
    var err error
    {% for v in variants %}
    err = json.Unmarshal(data, &u.variant_{{ v.name }})
    if err == nil {
        u.variant = "{{ v.name }}"
        return nil
    } else {
        u.variant_{{ v.name }} = nil
    }
    {% endfor %}
    return fmt.Errorf("invalid union variant: %s", string(data))
}

{% for v in variants %}
func {{ name }}__New{{ v.name|exported_name }}({% if v.literal_repr.is_none() %}v {{ v.type_.serialize_type(pkg) }}{% endif %}) {{ name }} {
    {% if let Some(literal_repr) = v.literal_repr %}
    var v {{ v.type_.serialize_type(pkg) }} = {{ literal_repr }}
    {% endif %}
    return {{ name }}{
        variant: "{{ v.name }}",
        variant_{{ v.name }}: &v,
    }
}

func (u *{{ name }}) Set{{ v.name|exported_name }}({% if v.literal_repr.is_none() %}v {{ v.type_.serialize_type(pkg) }}{% endif %}) {
    {% if let Some(literal_repr) = v.literal_repr %}
    var v {{ v.type_.serialize_type(pkg) }} = {{ literal_repr }}
    {% endif %}
    u.variant = "{{ v.name }}"
    u.variant_{{ v.name }} = &v
    {% for v2 in variants -%}
    {%- if v2.name != v.name %}
    u.variant_{{ v2.name }} = nil
    {% endif -%}
    {%- endfor %}
}

func (u *{{ name }}) Is{{ v.name|exported_name }}() bool {
    return u.variant == "{{ v.name }}"
}

func (u *{{ name }}) As{{ v.name|exported_name }}() *{{ v.type_.serialize_type(pkg) }} {
    if u.variant != "{{ v.name }}" {
        return nil
    }
    return u.variant_{{ v.name }}
}
{% endfor %}